package demo.impl;

import java.lang.reflect.ParameterizedType;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.pcap4j.packet.Packet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import demo.api.ProtocolService;
import demo.util.AssertUtils;

/**
 * 抽象协议基类
 * 
 * @author wangbin
 * @date 2017年8月4日
 * @param <P>
 *            协议对应的具体Packet类型
 */
public abstract class AbstractProtocolService<P extends Packet> implements ProtocolService {

	private static final Logger LOGGER = LoggerFactory.getLogger(AbstractProtocolService.class);

	/** 当前服务是否绑定在协议栈上 */
	private volatile boolean bound = false;

	protected Map<Number/* 上层协议类型 */, ProtocolService> highLevelProtocols = new ConcurrentHashMap<Number, ProtocolService>();

	private Class<P> parameterizedClass;

	/** 底层协议，对于链路层协议来说，底层协议位null */
	private ProtocolService baseProtocol;

	/** 是否记录捕获的包，仅仅是本层的开关，是否真正记录，还取决于底层协议是否已记录 */
	private boolean logPacketOption = false;

	public AbstractProtocolService(ProtocolService baseProtocol) {
		bind(baseProtocol);
	}

	/**
	 * 将当前服务绑定到底层协议上
	 * 
	 * @param baseProtocol
	 */
	protected void bind(ProtocolService baseProtocol) {
		this.baseProtocol = baseProtocol;
		this.bound = true;
		if (baseProtocol != null) {
			baseProtocol.installHighLevelProtocal(getProtocolType(), this);
		}
	}

	@Override
	public boolean isBound() {
		return bound;
	}

	@Override
	public void unbind() {
		if (bound) {
			LOGGER.info("关闭协议服务：protocol=" + getClass().getSimpleName());
			bound = false;
			if (baseProtocol != null) {
				baseProtocol.uninstallHighLevelProtocal(getProtocolType());
				baseProtocol = null;
			}
		}
	}

	/**
	 * 获取泛型参数类型
	 * 
	 * @return
	 */
	@SuppressWarnings("unchecked")
	protected Class<P> getParameterizedClass() {
		if (parameterizedClass == null) {
			ParameterizedType parameterizedType = (ParameterizedType) getClass().getGenericSuperclass();
			parameterizedClass = (Class<P>) parameterizedType.getActualTypeArguments()[0];
		}
		return parameterizedClass;
	}

	@SuppressWarnings("unchecked")
	@Override
	public <T extends ProtocolService> T getBaseProtocal() {
		return (T) baseProtocol;
	}

	/**
	 * 获得本协议的类型（用在下层协议上的协议分用标识）
	 * 
	 * @return
	 */
	protected abstract Number getProtocolType();

	/**
	 * 获取上层协议类型
	 * 
	 * @param packet
	 * @return
	 */
	protected abstract Number getHighLevelProtocolType(P packet);

	@Override
	public void gotPacket(Packet packet) {
		if (packet.contains(getParameterizedClass())) {
			if (needLogPacket()) {
				LOGGER.info(packet.toString());
			}

			Number highLevelProtocolType = getHighLevelProtocolType(packet.get(getParameterizedClass()));
			ProtocolService protocol = highLevelProtocols.get(highLevelProtocolType);
			if (protocol != null) {
				protocol.gotPacket(packet);
			}
		}
	}

	@Override
	public ProtocolService installHighLevelProtocal(Number type, ProtocolService highLevelProtocolService) {
		AssertUtils.notNull(type, "参数type不能为空！");
		AssertUtils.notNull(highLevelProtocolService, "参数highLevelProtocolService不能为空！");

		LOGGER.info("注册协议处理器：type=" + type + ", protocol=" + highLevelProtocolService);
		ProtocolService last = highLevelProtocols.put(type, highLevelProtocolService);
		if (last != null) {
			LOGGER.info("在注册协议处理器的同时，前一个同类型的协议处理器已失效：type=" + type + ", protocol=" + last);
		}
		return last;
	}

	@Override
	public ProtocolService uninstallHighLevelProtocal(Number type) {
		AssertUtils.notNull(type, "参数type不能为空！");

		ProtocolService last = highLevelProtocols.remove(type);
		if (last != null) {
			LOGGER.info("注销协议处理器：type=" + type + ", protocol=" + last);
		}
		return last;
	}

	@Override
	public ProtocolService getHighLevelProtocal(Number type) {
		AssertUtils.notNull(type, "参数type不能为空！");
		return highLevelProtocols.get(type);
	}

	@Override
	public void setLogPacketOption(boolean logPacket) {
		this.logPacketOption = logPacket;
	}

	@Override
	public boolean needLogPacket() {
		if (baseProtocol == null) {
			return logPacketOption;
		} else {
			return logPacketOption && !baseProtocol.needLogPacket();
		}
	}
}
